<template>
  <div :style="wrapperStyle" class="ruler-tool-wrapper" onselectstart="return false;">
    <section v-show="rulerToggle">
      <div
        class="ruler-tool-visible"
        @click.stop="lineToggle(!lineVisible)"
        :title="lineVisible ? '显示参考线' : '隐藏参考线'"
      >
        <hv-dynamic-icon
          :iconName="lineVisible ? 'SvgIconRefLine' : 'SvgIconRefLineInvisible'"
        />
      </div>
      <div
        ref="horizontalRulerRef"
        class="ruler-tool-h"
        @mousedown.stop="horizontalDragRuler"
      >
        <span
          v-for="(item, index) in xScale.value"
          :key="index"
          :style="{ left: index * 50 + 2 + 'px' }"
          class="n"
        >
          {{ item.id }}</span
        >
      </div>
      <div
        ref="verticalRulerRef"
        class="ruler-tool-v"
        @mousedown.stop="verticalDragRuler"
      >
        <span
          v-for="(item, index) in yScale.value"
          :key="index"
          :style="{ top: index * 50 + 2 + 'px' }"
          class="n"
        >
          {{ item.id }}</span
        >
      </div>
      <div :style="{ top: verticalDottedTop + 'px' }" class="ruler-tool-ref-dot-h"></div>
      <div
        :style="{ left: horizontalDottedLeft + 'px' }"
        class="ruler-tool-ref-dot-v"
      ></div>
      <div
        v-for="item in lineList"
        v-show="lineVisible"
        :title="item.title"
        :style="getLineStyle(item)"
        :key="item.id"
        :class="`ruler-tool-ref-line-${item.type}`"
        @mousedown="handleDragLine(item)"
      ></div>
    </section>
    <div ref="contentRef" class="ruler-tool-content" :style="contentStyle">
      <slot></slot>
    </div>
    <div v-show="isDrag" class="ruler-tool-content-mask"></div>
  </div>
</template>

<script lang="ts">
import {
  computed,
  defineComponent,
  getCurrentInstance,
  onMounted,
  onUnmounted,
  reactive,
  ref,
  watch,
} from "vue";
import { on, off } from "./event";

export default defineComponent({
  name: "RuleTool",
  props: {
    position: {
      type: String,
      default: "relative",
      validator: (val: string) => {
        return ["absolute", "fixed", "relative", "static", "inherit"].indexOf(val) !== -1;
      },
    }, // 规定元素的定位类型
    isHotKey: {
      type: Boolean,
      default: false,
    }, // 热键开关
    isScaleRevise: {
      type: Boolean,
      default: false,
    }, // 刻度修正(根据content进行刻度重置)
    value: {
      type: Array,
      default: () => {
        return []; // { type: 'h', site: 50 }, { type: 'v', site: 180 }
      },
    }, // 预置参考线
    contentLayout: {
      type: Object,
      default: () => {
        return { top: 0, left: 0 };
      },
    }, // 内容部分布局
    parent: {
      type: Boolean,
      default: false,
    },
    visible: {
      type: Boolean,
      default: true,
    },
    stepLength: {
      type: Number,
      default: 50,
      validator: (val: number) => val % 10 === 0,
    }, // 步长
  },
  emits: ["change", "update:value", "update:visible", "lineToggle"],
  setup(props, context) {
    const currentInstance: any = getCurrentInstance();

    const size = ref(17);
    const leftTop = ref(18); // 内容左上填充
    const windowWidth = ref(0); // 窗口宽度
    const windowHeight = ref(0); // 窗口高度
    const xScale: { value: any[] } = reactive<any>({
      value: [],
    }); // 水平刻度
    const yScale: { value: any[] } = reactive<any>({
      value: [],
    }); // 垂直刻度

    const topSpacing = ref(0); // 标尺与窗口上间距
    const leftSpacing = ref(0); //  标尺与窗口左间距
    const isDrag = ref(false);
    const dragFlag = ref(""); // 拖动开始标记，可能值x(从水平标尺开始拖动),y(从垂直标尺开始拖动)

    const horizontalDottedLeft = ref(-999); // 水平虚线位置
    const verticalDottedTop = ref(-999); // 垂直虚线位置
    const rulerWidth = ref(0); // 垂直标尺的宽度
    const rulerHeight = ref(0); // 水平标尺的高度
    const dragLineId = ref(""); // 被移动线的ID
    const lineVisible = ref(true); //参考线是否显示
    const keyCode = reactive({
      r: 82,
    }); // 快捷键参数
    const rulerToggle = ref(true); // 标尺辅助线显示开关

    const contentRef = ref();
    const verticalRulerRef = ref();
    const horizontalRulerRef = ref();

    watch(
      () => props.visible,
      () => {
        rulerToggle.value = props.visible;
      },
      { deep: false, immediate: true }
    );

    /**
     *  外层样式
     */
    const wrapperStyle: any = computed(() => {
      return {
        width: windowWidth.value + "px",
        height: windowHeight.value + "px",
        position: props.position,
      };
    });

    /**
     *  内容的样式
     */
    const contentStyle = computed(() => {
      return {
        left: props.contentLayout.left + "px",
        top: props.contentLayout.top + "px",
        padding: leftTop.value + "px 0px 0px " + leftTop.value + "px",
      };
    });

    /**
     * 参考线
     */
    const lineList = computed(() => {
      let hCount = 0;
      let vCount = 0;

      return props.value.map((item: any) => {
        const isH = item.type === "h";
        return {
          id: `${item.type}_${isH ? hCount++ : vCount++}`,
          type: item.type,
          title: item.site.toFixed(2) + "px",
          [isH ? "top" : "left"]: item.site / (props.stepLength / 50) + size.value,
        };
      });
    });

    const windowResize = () => {
      xScale.value = [];
      yScale.value = [];
      init();
    };

    const getLineStyle = ({ type, top, left }: any) => {
      return type === "h" ? { top: top + "px" } : { left: left + "px" };
    };

    const handleDragLine = ({ type, id }) => {
      return type === "h" ? dragHorizontalLine(id) : dragVerticalLine(id);
    };

    /**
     * 获取窗口宽与高
     */
    const box = () => {
      if (props.isScaleRevise) {
        // 根据内容部分进行刻度修正
        const content = contentRef.value;
        const contentLeft = content.offsetLeft;
        const contentTop = content.offsetTop;

        getCalcRevise(xScale.value, contentLeft);
        getCalcRevise(yScale.value, contentTop);
      }
      if (props.parent) {
        const parentEl =
          currentInstance.proxy.$el && currentInstance.proxy.$el.parentNode
            ? currentInstance.proxy.$el.parentNode
            : currentInstance.ctx.$el.parentNode;

        const style = window.getComputedStyle(parentEl, null);
        windowWidth.value = parseInt(style.getPropertyValue("width"), 10);
        windowHeight.value = parseInt(style.getPropertyValue("height"), 10);
      } else {
        windowWidth.value = document.documentElement.clientWidth - leftSpacing.value;
        windowHeight.value = document.documentElement.clientHeight - topSpacing.value;
      }
      rulerWidth.value = verticalRulerRef.value.clientWidth;
      rulerHeight.value = horizontalRulerRef.value.clientHeight;
      setSpacing();
    };

    const setSpacing = () => {
      topSpacing.value = horizontalRulerRef.value.getBoundingClientRect().y; //.offsetParent.offsetTop
      leftSpacing.value = verticalRulerRef.value.getBoundingClientRect().x; // .offsetParent.offsetLeft
    };

    /**
     *  计算刻度
     */
    const scaleCalc = () => {
      getCalc(xScale.value, windowWidth.value);
      getCalc(yScale.value, windowHeight.value);
    };

    /**
     * 获取刻度方法
     */
    const getCalc = (array: any, length: number) => {
      for (let i = 0; i < (length * props.stepLength) / 50; i += props.stepLength) {
        if (i % props.stepLength === 0) {
          array.push({ id: i });
        }
      }
    };

    /**
     * 获取矫正刻度方法
     */
    const getCalcRevise = (array: any, length: number) => {
      for (let i = 0; i < length; i += 1) {
        if (i % props.stepLength === 0 && i + props.stepLength <= length) {
          array.push({ id: i });
        }
      }
    };

    /**
     * 生成一个参考线
     */
    const newLine = (val: any) => {
      isDrag.value = true;
      dragFlag.value = val;
    };

    /**
     * 虚线移动
     */
    const dottedLineMove = ($event: any) => {
      setSpacing();
      switch (dragFlag.value) {
        case "x":
          if (isDrag.value) {
            verticalDottedTop.value = $event.pageY - topSpacing.value;
          }
          break;
        case "y":
          if (isDrag.value) {
            horizontalDottedLeft.value = $event.pageX - leftSpacing.value;
          }
          break;
        case "h":
          if (isDrag.value) {
            verticalDottedTop.value = $event.pageY - topSpacing.value;
          }
          break;
        case "v":
          if (isDrag.value) {
            horizontalDottedLeft.value = $event.pageX - leftSpacing.value;
          }
          break;
        default:
          break;
      }
    };

    /**
     * 更新modal value的值
     */
    const updateModelValue = (cloneList: any[]) => {
      context.emit("update:value", cloneList);
      context.emit("change", cloneList);
    };

    /**
     * 虚线松开
     */
    const dottedLineUp = ($event: any) => {
      setSpacing();
      if (isDrag.value) {
        isDrag.value = false;
        const cloneList = JSON.parse(JSON.stringify(props.value));
        switch (dragFlag.value) {
          case "x":
            //防止在刻度上画线
            if (leftTop.value + topSpacing.value >= $event.pageY) {
              break;
            }

            cloneList.push({
              type: "h",
              site:
                ($event.pageY - topSpacing.value - size.value) * (props.stepLength / 50),
            });
            updateModelValue(cloneList);
            break;
          case "y":
            //防止在刻度上画线
            if (leftTop.value + leftSpacing.value >= $event.pageX) {
              break;
            }
            cloneList.push({
              type: "v",
              site:
                ($event.pageX - leftSpacing.value - size.value) * (props.stepLength / 50),
            });
            updateModelValue(cloneList);
            break;
          case "h":
            dragCalc(cloneList, $event.pageY, topSpacing.value, rulerHeight.value, "h");
            updateModelValue(cloneList);
            break;
          case "v":
            dragCalc(cloneList, $event.pageX, leftSpacing.value, rulerWidth.value, "v");
            updateModelValue(cloneList);
            break;
          default:
            break;
        }
        verticalDottedTop.value = horizontalDottedLeft.value = -10;
      }
    };

    const dragCalc = (list: any, page: any, spacing: any, ruler: any, type: any) => {
      if (page - spacing < ruler) {
        let innerIndex: any;
        //let id: any;
        lineList.value.forEach((item, index) => {
          if (item.id === dragLineId.value) {
            innerIndex = index;
            //id = item.id;
          }
        });
        list.splice(innerIndex, 1, {
          type: type,
          site: -600,
        });
      } else {
        let innerIndex: any;
        //let id:any;
        lineList.value.forEach((item, index) => {
          if (item.id === dragLineId.value) {
            innerIndex = index;
            //id = item.id;
          }
        });
        list.splice(innerIndex, 1, {
          type: type,
          site: (page - spacing - size.value) * (props.stepLength / 50),
        });
      }
    };

    /**
     * 水平标尺处按下鼠标
     */
    const horizontalDragRuler = () => {
      newLine("x");
    };

    /**
     * 垂直标尺处按下鼠标
     */
    const verticalDragRuler = () => {
      newLine("y");
    };

    /**
     * 水平线处按下鼠标
     */
    const dragHorizontalLine = (id: any) => {
      isDrag.value = true;
      dragFlag.value = "h";
      dragLineId.value = id;
    };

    /**
     * 垂直线处按下鼠标
     */
    const dragVerticalLine = (id: any) => {
      isDrag.value = true;
      dragFlag.value = "v";
      dragLineId.value = id;
    };

    /**
     * 键盘事件
     */
    const keyboard = ($event: any) => {
      if (props.isHotKey) {
        switch ($event.keyCode) {
          case keyCode.r:
            rulerToggle.value = !rulerToggle.value;
            context.emit("update:visible", rulerToggle.value);
            if (rulerToggle.value) {
              leftTop.value = 18;
            } else {
              leftTop.value = 0;
            }
            break;
        }
      }
    };

    /**
     * 线切换
     */
    const lineToggle = (val: boolean) => {
      lineVisible.value = val;
      context.emit("lineToggle", lineVisible.value);
    };

    /**
     * 设置线
     */
    const setLineVisible = (val: boolean) => {
      lineVisible.value = val;
    };

    /**
     * 清除参考线
     */
    const clearLine = () => {
      updateModelValue([]);
    };

    /**
     * 获取内容尺寸
     */
    const getContentDimensions = () => {
      return {
        width: windowWidth.value - leftTop.value,
        height: windowHeight.value - leftTop.value,
      };
    };

    /**
     * 初始化
     */
    const init = () => {
      box();
      scaleCalc();
    };

    /**
     * 绑定事件
     */
    const bindEvents = () => {
      on(document, "mousemove", dottedLineMove);
      on(document, "mouseup", dottedLineUp);
      on(document, "keyup", keyboard);
      init();
      on(window, "resize", windowResize);
    };

    /**
     * 解绑事件
     */
    const unBindEvents = () => {
      off(document, "mousemove", dottedLineMove);
      off(document, "mouseup", dottedLineUp);
      off(document, "keyup", keyboard);
      off(window, "resize", windowResize);
    };

    onMounted(() => {
      bindEvents();
    });

    onUnmounted(() => {
      unBindEvents();
    });

    return {
      wrapperStyle,
      contentStyle,
      lineList,
      rulerToggle,
      isDrag,
      xScale,
      yScale,
      verticalDottedTop,
      horizontalDottedLeft,
      contentRef,
      verticalRulerRef,
      horizontalRulerRef,
      horizontalDragRuler,
      verticalDragRuler,
      getLineStyle,
      handleDragLine,
      lineVisible,
      lineToggle,
      setLineVisible,
      clearLine,
      getContentDimensions,
    };
  },
});
</script>

<style lang="less" scoped>
@import "./RuleTool.less";
</style>
